home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-01 / phmacro.zip / MAC.C < prev    next >
Text File  |  1993-01-04  |  16KB  |  492 lines

  1. /* macros - skeleton of macro expansion algorithm
  2.  * Each token is one char (letter a-z).
  3.  * Token-buffers are modeled by plain old char strings.
  4.  * Each such buffer has another same-size buffer printed just under it,
  5.  * which shows the "hide-set" associated with the token above it.
  6.  * (The "hide-set" contains all the tokens that are non-replaceable.)
  7.  * #define  is  #d (no space before or after #).
  8.  * No attempt to model details of whitespace handling.
  9.  * Two tokens, when catenated, produce "second token plus one".
  10.  * "Stringize" is crudely stubbed; two quotes "" replace the string.
  11.  * Does not handle empty actuals gracefully.
  12.  * Does not span newlines in matching actuals.
  13.  * Revisions:
  14.  * 88/10/01: Identify the "replacement" buffer as "R".
  15.  * 88/10/03: Handle the hide-set of actuals properly.
  16.  * 88/10/03: Indicate where to refine for the exact (unspecified)
  17.  *             semantics of interactions of hide-sets in catenation
  18.  */
  19. /*
  20.  * First version written by Thomas Plum, Plum Hall Inc.
  21.  * Subsequently revised by members of X3J11, the
  22.  * ANSI C Standards Committee.
  23.  * Permission is granted to reproduce and use this program,
  24.  * for all purposes, provided that this notice is included.
  25.  */
  26.  
  27. /* First, 300+ lines of inelegant support routines, to about 363  ... */
  28.  
  29. #include <stdio.h>
  30. #include <ctype.h>
  31. #include <string.h>
  32. #define TRACE(x) 0     /* or,  printf x , if needed for debugging */
  33. #define OUT(x) printf x
  34. #define cpy_nam(p, q) (p[0] = *(q), p[1] = '\0', ++(q))
  35. #define eobuf(p) (*(p) == '\0')
  36. #define next(p, s) (strncmp(p, s, (length_matched = strlen(s))) == 0)
  37. #define advance(p) (p += length_matched)
  38. #define is_obj(p) in_set(obj.nam, *(p))
  39. #define is_fn(p) in_set(fn.nam, *(p))
  40. #define obj_def(p) obj.def[obj_num(p)][0]
  41. #define obj_num(p) (strchr(obj.nam, *(p)) - obj.nam)
  42. #define fn_def(p) fn.def[fn_num(p)][0]
  43. #define fn_num(p) (strchr(fn.nam, *(p)) - fn.nam)
  44. #define fn_parm_count(p) strlen(fn.parms[fn_num(p)])
  45. #define fn_parm_index(p, q) \
  46.        (strchr(fn.parms[fn_num(p)], *(q)) - fn.parms[fn_num(p)])
  47. #define is_parm_name(p, q) in_set(fn.parms[fn_num(p)], *(q))
  48. #define in_set(p, c) (strchr(p, c) != 0)
  49. #define hide_set(p) *((char*)p + L)
  50. #define A 9   /* max # of macro args */
  51. #define D 26  /* max # of macro defs */
  52. #define L 100 /* max length of an input line  or of a macro def */
  53. int length_matched;
  54. void diagram(), expand(), expand_fn(), set_hide(), listcpy();
  55. void add_hide(), lower_case();
  56. char *h_set();
  57. char arg_patterns[10][24] =
  58.        {
  59.        "()",
  60.        "(_)",
  61.        "(_,_)",
  62.        "(_,_,_)",
  63.        "(_,_,_,_)",
  64.        "(_,_,_,_,_)",
  65.        "(_,_,_,_,_,_)",
  66.        "(_,_,_,_,_,_,_)",
  67.        "(_,_,_,_,_,_,_,_)",
  68.        "(_,_,_,_,_,_,_,_,_)",
  69.        };
  70. char next_token(p) char *p;
  71.        {
  72.        length_matched = 0;
  73.        ++p;
  74.        for (;;)
  75.                {
  76.                if (*p == ' ')
  77.                        ++p, ++length_matched;
  78.                else if (*p == '\0')
  79.                        {
  80.                        printf("treating end-of-buffer as end-of-file\n");
  81.                        return *p;
  82.                        }
  83.                else
  84.                        return *p;
  85.                }
  86.        }
  87.  
  88. void strip_blanks(p) char *p;
  89.        {
  90.        char *q = p;
  91.  
  92.        while (*p == ' ')
  93.                ++p;
  94.        /* now at first non-blank in p */
  95.        while (*p != '\0')      /* can't use strcpy because overlap */
  96.                hide_set(q) = hide_set(p), *q++ = *p++;
  97.        while (*--q == ' ')
  98.                ;
  99.        *++q = '\0', hide_set(q) = '\0';
  100.        }
  101.  
  102. struct obj { int n; char nam[D]; char def[D][2][L]; }
  103.        obj = {0};
  104. struct fn  { int n; char nam[D]; char def[D][2][L]; char parms[D][A]; }
  105.        fn = {0};
  106.  
  107. void install_obj(nam, def) char *nam; char *def;
  108.        {
  109.        obj.nam[obj.n] = nam[0];
  110.        strcpy(obj.def[obj.n][0], def);
  111.        strip_blanks(obj.def[obj.n][0]);
  112.        set_hide(obj.def[obj.n][0], nam);
  113.        printf("obj: nam=<%s> def=<%s>\n", nam, obj.def[obj.n][0]);
  114.        printf("                 <%s>\n", obj.def[obj.n][1]);
  115.        ++obj.n;
  116.        }
  117.  
  118. void install_fn(nam, def, parms) char *nam; char *def; char *parms;
  119.        {
  120.        fn.nam[fn.n] = nam[0];
  121.        strcpy(fn.def[fn.n][0], def);
  122.        strcpy(fn.parms[fn.n], parms);
  123.        strip_blanks(fn.def[fn.n][0]);
  124.        set_hide(fn.def[fn.n][0], nam);
  125.        printf("fn:  nam=<%s> parms=<%s> def=<%s>\n", nam, parms, fn.def[fn.n][0]);
  126.        printf("                    %*s      <%s>\n",
  127.                strlen(parms), "", fn.def[fn.n][1]);
  128.        ++fn.n;
  129.        }
  130.  
  131. char *parse_parms(p, parms) char *p; char *parms;
  132.        {
  133.        int i = 0;
  134.  
  135.        while (*p != ')')
  136.                {
  137.                if (*p != ' ' && *p != ',')
  138.                        parms[i++] = *p;
  139.                ++p;
  140.                }
  141.        ++p;
  142.        parms[i] = '\0';
  143.        return p;
  144.        }
  145.  
  146.  
  147.  
  148. void replace(level, suf, buf, p, p2, def) char *level, *suf, *buf, *p, *p2, *def;
  149.        {
  150.        char hold[2][L];
  151.        char old_hide[2];
  152.  
  153.        listcpy(hold[0], p2);
  154.        old_hide[0] = hide_set(p), old_hide[1] = '\0';
  155.        listcpy(p, def);
  156.        add_hide(p, old_hide);
  157.        listcpy(p + strlen(def), hold[0]);
  158.        diagram(level, buf, suf);
  159.        }
  160. char *match_actuals(p, actual, n) char *p; char actual[A][2][L]; int n;
  161.        {
  162.        int parens = 1;
  163.        int i = 0;
  164.        int j = 0;
  165.  
  166.        TRACE(("match_actuals(<%s>, actual)\n", p));
  167.        p += 1;
  168.        /* past the '(' */
  169.  
  170.        for ( ;; )
  171.                {
  172.                if (*p == '(')
  173.                        {
  174.                        ++parens;
  175.                        actual[i][1][j] = p[L];
  176.                        actual[i][0][j++] = *p++;
  177.                        }
  178.                else if (*p == ')')
  179.                        {
  180.                        --parens;
  181.                        if (parens == 0)
  182.                                break;
  183.                        actual[i][1][j] = p[L];
  184.                        actual[i][0][j++] = *p++;
  185.                        }
  186.                else if (*p == ',' && parens == 1)
  187.                        {
  188.                        actual[i][1][j] = '\0';
  189.                        actual[i][0][j] = '\0';
  190.                        j = 0;
  191.                        ++i;
  192.                        ++p;
  193.                        }
  194.                else
  195.                        {
  196.                        actual[i][1][j] = p[L];
  197.                        actual[i][0][j++] = *p++;
  198.                        }
  199.                }
  200.        actual[i][1][j] = '\0';
  201.        actual[i][0][j] = '\0';
  202.        /* should strip blanks on each actual, to be sure non-empty */
  203.        if (i == 0 && n == 0)
  204.                ;       /* ok, no args */
  205.        else if (i != n-1)
  206.                {
  207.                printf("wrong number of actuals\n");
  208.                exit(2);
  209.                }
  210.        for (i = 0; i < n; ++i)
  211.                TRACE(("actual[%d] = <%s>\n", i, actual[i][0]));
  212.        return p+1;
  213.        }
  214.  
  215. char *stringize(s) char *s;
  216.        {
  217.        static char string_buf[2][L] = {"\"\"", "  "};
  218.  
  219.        printf("string \"%s\" --> \"\"\n", s);
  220.        return string_buf[0];
  221.        }
  222.  
  223. char *catenate(p, q) char *p, *q;
  224.        {
  225.        static char cat_buf[2][L];
  226.        char old_hide[2];
  227.  
  228.        cat_buf[0][0] = q[0] + 1;
  229.        cat_buf[0][1] = '\0';
  230.        set_hide(cat_buf[0], " ");
  231.        old_hide[0] = hide_set(p), old_hide[1] = '\0';
  232.        add_hide(cat_buf[0], old_hide);
  233.        old_hide[0] = hide_set(q), old_hide[1] = '\0';
  234.        add_hide(cat_buf[0], old_hide); /* NOTE: The other "unspecified" */
  235.                                                                        /* choice is to intersect(!) the */
  236.                                                                        /* two hide-sets, as in Prosser  */
  237.                                                                        /* 86-196.                       */
  238.        printf("catenate %c%c --> %c\n", p[0], q[0], cat_buf[0][0]);
  239.        return cat_buf[0];
  240.        }
  241.  
  242. void listcpy(p, q) char *p, *q;
  243.        {
  244.        strcpy(p, q);
  245.        strcpy(&hide_set(p), &hide_set(q));
  246.        }
  247.  
  248. void diagram(level, s, suf) char *level, *s, *suf;
  249.        {
  250.        char prefix[L];
  251.  
  252.        if (level[0] == '\0')
  253.                strcpy(prefix, "0");
  254.        else
  255.                strcpy(prefix, level+1);
  256.        printf("%s%s: %s\n", prefix, suf, s);
  257.        printf("%*s%.*s\n", strlen(prefix)+strlen(suf)+2, ": ",
  258.                strlen(s), &hide_set(s));
  259.        }
  260.  
  261. int charcmp(s, t) char *s, *t;
  262.        {
  263.        return *s - *t;
  264.        }
  265.  
  266. void set_hide(def, nam) char *def, *nam;
  267.        {
  268.        memset(&hide_set(def), nam[0], strlen(def));
  269.        def[L+strlen(def)] = '\0';
  270.        }
  271. char hide_sets[10][10] = {0};
  272. int n_hide_sets = 0;
  273. void add_hide(p, h) char *p, *h;
  274.        {
  275.        int i, lim;
  276.  
  277.        lim = strlen(p);
  278.        for (i = 0; i < lim; ++i)
  279.                {
  280.                char c = p[L+i];
  281.                char old_h[2];
  282.  
  283.                old_h[0] = c, old_h[1] = '\0';
  284.                TRACE(("c=<%c>, h=<%c>\n", c, h[0]));
  285.                if (h[0] == ' ')
  286.                        ;
  287.                else if (c == ' ')
  288.                        p[L+i] = h[0];
  289.                else if (h[0] == c)
  290.                        ;
  291.                else if (in_set(h_set(old_h), h[0]))
  292.                        ;
  293.                else
  294.                        {
  295.                        char new_hide[10];
  296.                        int n = n_hide_sets;
  297.                        int j;
  298.                        int found = 0;
  299.  
  300.                        strcpy(new_hide, h_set(old_h));
  301.                        strcat(new_hide, h_set(h));
  302.                        qsort(new_hide, strlen(new_hide), 1, charcmp);
  303.                        TRACE(("new_hide=<%s>\n", new_hide));
  304.                        for (j = 0; j < n_hide_sets; ++j)
  305.                                {
  306.                                if (strcmp(new_hide, hide_sets[j]) == 0)
  307.                                        {
  308.                                        p[L+i] = j + '0';
  309.                                        found = 1;
  310.                                        }
  311.                                }
  312.                        if (!found)
  313.                                {
  314.                                if (n > 9)
  315.                                        {
  316.                                        printf("too many hide-sets\n");
  317.                                        exit(2);
  318.                                        }
  319.                                strcpy(hide_sets[n], new_hide);
  320.                                p[L+i] = n + '0';
  321.                                qsort(hide_sets[n], strlen(hide_sets[n]), 1, charcmp);
  322.                                printf("hide-set #%d = {%s}\n", n, hide_sets[n]);
  323.                                ++n_hide_sets;
  324.                                }
  325.                        }
  326.                }
  327.        }
  328.  
  329. char *h_set(s) char *s;
  330.        {
  331.        char c = s[0];
  332.  
  333.        if (isdigit(c))
  334.                return hide_sets[c - '0'];
  335.        else
  336.                return s;
  337.        }
  338.  
  339. int is_hidden(p) char *p;
  340.        {
  341.        if (!islower(*p))
  342.                return 0;
  343.        else if (*p == p[L])
  344.                return 1;
  345.        else if (isdigit(p[L]) && strchr(hide_sets[p[L] - '0'], *p) != 0)
  346.                return 1;
  347.        else
  348.                return 0;
  349.        }
  350.  
  351. void mark_non_replace(p) char *p;
  352.        {
  353.        *p = toupper(*p);
  354.        }
  355.  
  356. void lower_case(p) char *p;
  357.        {
  358.        for ( ; *p != '\0'; ++p)
  359.                *p = tolower(*p);
  360.        }
  361.  
  362.  
  363. /* Now: Here's the actual macro algorithm ... */
  364.  
  365. void preproc(p) char *p;
  366.        {
  367.        char nam[2];
  368.        char parms[A];
  369.  
  370.        if (next(p, "#d "))
  371.                {                       /* starting a #define */
  372.                advance(p);
  373.                TRACE(("p=<%s>\n", p));
  374.                cpy_nam(nam, p);
  375.                if (next(p, "("))       /* a fn-like macro */
  376.                        {
  377.                        advance(p);
  378.                        p = parse_parms(p, parms);
  379.                        install_fn(nam, p, parms);
  380.                        }
  381.                else            /* an object-like macro */
  382.                        install_obj(nam, p);
  383.                }       /* end of #define */
  384.        else if (next(p, "#u "))
  385.                {                       /* starting a #undef */
  386.                } /* stub */
  387.        else
  388.                {
  389.                expand(p, "");
  390.                lower_case(p);
  391.                set_hide(p, " ");
  392.                diagram("", p, "");
  393.                }
  394.        }
  395. void expand(buf, level) char *buf; char *level;
  396.        {
  397.        char *p = buf;
  398.  
  399.        TRACE(("expand(<%s>, %s)\n", buf, level));
  400.        diagram(level, buf, "");
  401.        while (!eobuf(p))
  402.                {
  403.                if (is_hidden(p))
  404.                        {
  405.                        mark_non_replace(p);
  406.                        ++p;
  407.                        diagram(level, buf, "");
  408.                        }
  409.                else if (is_obj(p))     /* instance of object-like macro */
  410.                        replace(level, "", buf, p, p+1, obj_def(p));
  411.                else if (is_fn(p) && next_token(p) == '(')
  412.                        expand_fn(buf, p, level); /* instance of fn-like macro */
  413.                else
  414.                        ++p;    /* ordinary token */
  415.                }       /* end while !eobuf */
  416.        }       /* end expand() */
  417.  
  418.  
  419.  
  420.  
  421.  
  422.  
  423. void expand_fn(buf, p, level) char *buf, *p, *level;
  424.        {
  425.        char actual[A][2][L], expandeds[A][2][L];
  426.        char repl[2][L];
  427.        char nlevel[20];
  428.        char fn_nam[2];
  429.        char invocation[2][L];
  430.        char *start_invok, *q;
  431.        int i_parm, num_parms;
  432.  
  433.        start_invok = p;
  434.        cpy_nam(fn_nam, p);
  435.        advance(p);     /* past any blanks skipped in next_token */
  436.        num_parms = fn_parm_count(fn_nam);
  437.        p = match_actuals(p, actual, num_parms);
  438.        for (i_parm = 0; i_parm < num_parms; ++i_parm)
  439.                {
  440.                listcpy(expandeds[i_parm][0], actual[i_parm][0]);
  441.                sprintf(nlevel, "%s.%d", level, i_parm+1);
  442.                expand(expandeds[i_parm][0], nlevel);
  443.                }
  444.        sprintf(invocation[0], "%s%s", fn_nam, arg_patterns[num_parms]);
  445.        set_hide(invocation[0], " ");
  446.        diagram(level, invocation[0], "R");
  447.        listcpy(repl[0], fn_def(fn_nam));
  448.        diagram(level, repl[0], "R");
  449.        TRACE(("subst parms in repl:<%s>\n", repl));
  450.        for (q = repl[0]; !eobuf(q); )
  451.                {
  452.                TRACE(("repl-token <%c>\n", *q));
  453.                if (q[1] == '#' && q[2] == '#' && !eobuf(q+3))
  454.                        {
  455.                        replace(level, "R", repl[0], q, q+4,
  456.                                catenate(q, q+3));
  457.                        q += 1; /* advance past new "token" */
  458.                        }
  459.                else if (q[0] == '#' && is_parm_name(fn_nam, q+1))
  460.                        {
  461.                        i_parm = fn_parm_index(fn_nam, q+1);
  462.                        replace(level, "R", repl[0], q, q+2,
  463.                                stringize(actual[i_parm][0]));
  464.                        q += 2; /* advance past "" */
  465.                        }
  466.                else if (is_parm_name(fn_nam, q))
  467.                        {
  468.                        i_parm = fn_parm_index(fn_nam, q);
  469.                        replace(level, "R", repl[0], q, q+1,
  470.                                expandeds[i_parm][0]);
  471.                        q += strlen(expandeds[i_parm][0]);      /* advance past expansion */
  472.                        }
  473.                else            /* ordinary token */
  474.                        ++q;
  475.                }
  476.        replace(level, "", buf, start_invok, p, repl[0]);
  477.        }
  478.  
  479.  
  480.  
  481.  
  482. main()
  483.        {
  484.        char line[BUFSIZ];
  485.  
  486.        while (gets(line))
  487.                {
  488.                set_hide(line, " ");
  489.                preproc(line);
  490.                }
  491.        } /* end main */
  492.